using System;
using System.ComponentModel;
using LocalOverride.Fiddler;
using LocalOverride.Fiddler.Store;
using LocalOverride.Fiddler.UI;
using NSubstitute;
using NUnit.Framework;
using System.Linq;

namespace LocalOverride.Tests.UI
{
    [TestFixture]
    public class MappingEditorPresenterTests
    {
        private IMappingEditorView view;
        private IMappingStore store;

        [SetUp]
        public void SetUp()
        {
            view = Substitute.For<IMappingEditorView>();
            store = Substitute.For<IMappingStore>();
        }

        [Test]
        public void AddOverrideShowsOverrideScreen()
        {
            InitializePresenter(true);

            view.AddOverride += Raise.Event(view, EventArgs.Empty);

            view.Received().ShowOverrideEditor();
        }

        [Test]
        public void AddOverrideClearsErrors()
        {
            InitializePresenter(true);

            view.AddOverride += Raise.Event(view, EventArgs.Empty);

            view.Received().ClearErrors();
        }

        [Test]
        public void AddOverride_BlanksEditFields()
        {
            InitializePresenter(true);

            view.AddOverride += Raise.Event(view, EventArgs.Empty);

            Assert.AreEqual(string.Empty, view.EditUrl);
            Assert.AreEqual(string.Empty, view.EditLocalPath);
            Assert.IsFalse(view.IsDirectory);
        }

        [Test]
        public void EditOverride_ShowsOverrideScreen()
        {

            InitializePresenter(true);

            var mappingEntry = new MappingEntry();

            view.EditOverride += Raise.Event(view, new EditOverrideEventArgs(mappingEntry));

            view.Received().ShowOverrideEditor();
        }

        [Test]
        public void EditOverride_SetsErrors_WhenMappingIsInvalid()
        {
            InitializePresenter(false);

            var mappingEntry = new MappingEntry();

            view.EditOverride += Raise.Event(view, new EditOverrideEventArgs(mappingEntry));

            view.ReceivedWithAnyArgs().SetErrors(null);
        }

        [Test]
        public void EditOverride_ClearsErrors_WhenMappingIsValid()
        {
            InitializePresenter(true);
            
            var mappingEntry = new MappingEntry();

            view.EditOverride += Raise.Event(view, new EditOverrideEventArgs(mappingEntry));

            view.ReceivedWithAnyArgs().ClearErrors();
        }

        private void InitializePresenter(bool onValidate)
        {
            store.Entries.Returns(new MappingEntry[0]);


            if (onValidate)
            {
                store.ValidatorFor(null).ReturnsForAnyArgs(new PassingValidator());
            }
            else
            {
                store.ValidatorFor(null).ReturnsForAnyArgs(new FailingValidator());
            }
            new MappingEditorPresenter(view, store);
        }

        [Test]
        public void CancelOverrideEdit_HidesOverrideScreen()
        {
            InitializePresenter(true);

            view.CancelOverrideEdit += Raise.Event(view, EventArgs.Empty);

            view.Received().HideOverrideEditor();
        }

        [Test]
        public void SaveOverrideEdit_HidesOverrideScreen()
        {
            SetViewToReturnValidEntry();

            InitializePresenter(true);

            view.SaveOverrideEdit += Raise.Event(view, EventArgs.Empty);

            view.Received().HideOverrideEditor();
        }

        private void SetViewToReturnValidEntry()
        {
            view.EditLocalPath = @"c:\temp";
            view.EditUrl = "http://test.com";
            view.IsDirectory = false;
            view.DownloadLocalCopy = false;
        }

        [Test]
        public void SaveOverrideEdit_PersistsMappingStore()
        {
            SetViewToReturnValidEntry();

            InitializePresenter(true);
            
            view.SaveOverrideEdit += Raise.Event(view, EventArgs.Empty);

            store.Received().Persist();
        }

        [Test]
        public void SaveOverrideEdit_UpdatesMappings()
        {
            InitializePresenter(true);

            view.SaveOverrideEdit += Raise.Event(view, EventArgs.Empty);

            view.ReceivedWithAnyArgs().MappingList = new BindingList<MappingEntry>();
        }

        [Test]
        public void DeleteOverride_DeletesEntryFromStore()
        {
            InitializePresenter(true);

            var mappingEntry = new MappingEntry
                                   {
                                       LocalPath = "c:\blah"
                                   };
            view.DeleteOverride += Raise.Event(view, new DeleteOverrideEventArgs(mappingEntry));

            store.Received().RemoveEntry(mappingEntry);
        }

        [Test]
        public void DeleteOverride_PersistsStore()
        {
            InitializePresenter(true);

            var mappingEntry = new MappingEntry
            {
                LocalPath = "c:\blah"
            };
            view.DeleteOverride += Raise.Event(view, new DeleteOverrideEventArgs(mappingEntry));

            store.Received().Persist();
        }

        [Test]
        public void DeleteOverride_UpdatesMappings()
        {
            InitializePresenter(true);

            var mappingEntry = new MappingEntry
            {
                LocalPath = "c:\blah"
            };
            view.DeleteOverride += Raise.Event(view, new DeleteOverrideEventArgs(mappingEntry));

            view.ReceivedWithAnyArgs().MappingList = null;
        }

        [Test]
        public void SaveOverrideEdit_AddsEntryToStore_WhenNewOverrideAfterEdit()
        {
            InitializePresenter(true);

            view.EditOverride += Raise.Event(view, new EditOverrideEventArgs(new MappingEntry()));

            view.AddOverride += Raise.Event(view, EventArgs.Empty);

            SetValidEditValues();

            view.SaveOverrideEdit += Raise.Event(view, EventArgs.Empty);
            store.ReceivedWithAnyArgs().AddEntry(null);
        }

        [Test]
        public void SaveOverrideEdit_RevertsMappingEntryChangesWhenInvalid()
        {
            InitializePresenter(false);


            var testMappingEntry = new MappingEntry
                                       {
                                           LocalPath = "c:\temp2",
                                           Url = "http://test2.com",
                                           IsDirectory = true
                                       };
            view.EditOverride += Raise.Event(view, new EditOverrideEventArgs(testMappingEntry));

            view.EditUrl += "blah";
            view.EditLocalPath += "blah";
            view.IsDirectory = false;

            view.SaveOverrideEdit += Raise.Event(view, EventArgs.Empty);

            Assert.AreEqual("c:\temp2", testMappingEntry.LocalPath);
            Assert.AreEqual("http://test2.com", testMappingEntry.Url);
            Assert.AreEqual(true, testMappingEntry.IsDirectory);
        }


        private void SetValidEditValues()
        {
            view.EditUrl = @"http://test.com";
        }

        [Test]
        public void SaveOverrideEdit_EditsEntryInStore_WhenEditingOverride()
        {
            var mappingEntry = new MappingEntry
            {
                LocalPath = @"c:\some\test\file\path.txt",
                IsDirectory = true,
                Url = @"http://blah.com"
            };
            var mappingEntries = new[] { mappingEntry };

            var mappingList = GetUIMappingListAfterPresenterInitialization(mappingEntries);

            view.EditOverride += Raise.Event(view, new EditOverrideEventArgs(mappingList.First()));

            const string url = @"http://test.com";
            const string localFile = @"c:\temp\localfile";
            view.EditUrl = url;
            view.EditLocalPath = localFile;
            view.IsDirectory = true;
            view.SaveOverrideEdit += Raise.Event(view, EventArgs.Empty);

            Assert.AreEqual(url, mappingEntry.Url);
            Assert.AreEqual(localFile, mappingEntry.LocalPath);
            Assert.IsTrue(mappingEntry.IsDirectory);
        }

        [Test]
        public void OnLoadHideOverrideScreen()
        {
            InitializePresenter(true);

            view.Received().HideOverrideEditor();
        }

        [Test]
        public void OnLoadDataSourceHasItemsFromStore()
        {
            var mappingEntries = new[] { new MappingEntry(), new MappingEntry(), new MappingEntry() };

            BindingList<MappingEntry> mappingList = GetUIMappingListAfterPresenterInitialization(mappingEntries);

            Assert.IsNotNull(mappingList);
            const int mappingCount = 3;
            Assert.AreEqual(mappingCount, mappingList.Count);
        }

        private BindingList<MappingEntry> GetUIMappingListAfterPresenterInitialization(MappingEntry[] mappingEntries)
        {
            store.Entries.Returns(mappingEntries);

            store.ValidatorFor(null).ReturnsForAnyArgs(new PassingValidator());

            BindingList<MappingEntry> mappingList = null;

            view.WhenForAnyArgs(x => x.MappingList = null).Do(x => mappingList = x.Arg<BindingList<MappingEntry>>());

            new MappingEditorPresenter(view, store);
            return mappingList;
        }

        [Test]
        public void MappingEntriesAreMappedCorrectlyFromStoreToUi()
        {
            var mappingEntry = new MappingEntry
            {
                LocalPath = @"c:\some\test\file\path.txt",
                IsDirectory = true,
                Url = @"http://blah.com"
            };
            var mappingEntries = new[] { mappingEntry };

            BindingList<MappingEntry> mappingList = GetUIMappingListAfterPresenterInitialization(mappingEntries);

            var mapping = mappingList.First();
            Assert.AreEqual(mappingEntry.LocalPath, mapping.LocalPath);
            Assert.AreEqual(mappingEntry.Url, mapping.Url);
            Assert.AreEqual(mappingEntry.IsDirectory, mapping.IsDirectory);
        }

        [Test]
        public void WhenInvalidUrl_SaveNew_DoesNotStoreMapping()
        {
            SaveInvalidUrl();
            store.DidNotReceiveWithAnyArgs().AddEntry(null);
        }

        private void SaveInvalidUrl()
        {
            const string invalidUrl = "http:222//blah.com";
            view.EditUrl = invalidUrl;
            view.DownloadLocalCopy = false;

            InitializePresenter(false);
            
            view.AddOverride += Raise.Event(view, EventArgs.Empty);

            view.ClearReceivedCalls();

            view.SaveOverrideEdit += Raise.Event(view, EventArgs.Empty);
        }

        private void SaveInvalidExtension()
        {
            const string url = "http://blah.com";
            view.EditUrl = url;
            const string invalidExtensions = "$;##";
            view.EditExtensions = invalidExtensions;
            view.DownloadLocalCopy = false;

            InitializePresenter(false);
            view.AddOverride += Raise.Event(view, EventArgs.Empty);

            view.ClearReceivedCalls();

            view.SaveOverrideEdit += Raise.Event(view, EventArgs.Empty);
        }

        [Test]
        public void WhenInvalidUrl_SaveNew_SetsError()
        {
            SaveInvalidUrl();
            view.ReceivedWithAnyArgs().SetErrors(new[] { new ErrorNotification("Url", "Invalid Url") });
        }

        [Test]
        public void WhenInvalidUrl_SaveNew_DoesNotHideEditForm()
        {
            SaveInvalidUrl();
            view.DidNotReceiveWithAnyArgs().HideOverrideEditor();
        }

        [Test]
        public void WhenInvalidUrl_SaveNew_DoesNotPersist()
        {
            SaveInvalidUrl();
            store.DidNotReceiveWithAnyArgs().Persist();
        }

        [Test]
        public void WhenInvalidExtensions_SaveNew_SetsError()
        {
            SaveInvalidExtension();
            view.ReceivedWithAnyArgs().SetErrors(new[] { new ErrorNotification("Extensions", "Invalid Extensions") });
        }

        [Test]
        public void WhenInvalidExtensions_SaveNew_DoesNotHideEditForm()
        {
            SaveInvalidExtension();
            view.DidNotReceiveWithAnyArgs().HideOverrideEditor();
        }

        [Test]
        public void WhenInvalidExtensions_SaveNew_DoesNotPersist()
        {
            SaveInvalidExtension();
            store.DidNotReceiveWithAnyArgs().Persist();
        }
    }

    internal class FailingValidator : IValidator
    {
        public ValidationResult Validate()
        {
            return new ValidationResult(false, new ErrorNotification("FieldName", "Error Message"));
        }
    }

    internal class PassingValidator : IValidator
    {
        public ValidationResult Validate()
        {
            return ValidationResult.Valid;
        }
    }
}